import asyncio
import logging
import sys
from typing import Optional, Sequence, Type

from beanie import PydanticObjectId, init_beanie
from beanie.odm.documents import (Document, document_alias_generator,
                                  json_schema_extra)
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel, ConfigDict, Field
from tortoise import Tortoise

from fastapi_template.config import app_config

init_db_called = False


def motor_client():
    client = AsyncIOMotorClient(str(app_config.DEFAULT_MONGO_URI))
    if hasattr(sys, "_called_from_test"):
        logging.info("init db from test")
        client.get_io_loop = asyncio.get_event_loop
    return client


def motor_db():
    return motor_client()[app_config.DEFAULT_MONGO_DBNAME]


async def init_db(models: Sequence[Type[Document]]):
    global init_db_called
    if init_db_called:
        return

    sqlite_url = "sqlite://data/db.sqlite3"

    await Tortoise.init(
        db_url=app_config.POSTGRES_DSN.unicode_string()
        if app_config.POSTGRES_DSN else sqlite_url,
        modules={"models": ["fastapi_template.models"]},
    )
    await Tortoise.generate_schemas()

    if app_config.DEFAULT_MONGO_URI:
        db = motor_db()
        await init_beanie(database=db, document_models=models)

    init_db_called = True
    logging.info("init db done")


class PydanticObjectIdAutoModel(BaseModel):
    id: Optional[PydanticObjectId] = Field(
        default=None, description="MongoDB document ObjectID")

    model_config = ConfigDict(
        json_schema_extra=json_schema_extra,
        populate_by_name=True,
        alias_generator=document_alias_generator,
    )
